﻿using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Data.SqlClient;
using System.Collections.Generic;
using System.Diagnostics;
using DynORM.Interfaces.BLLMapper.Enum;
using DynORM.BLLMapper;
using DynORM.Enums;
using DynORM.Interfaces;

namespace UnitTestProject
{
    [TestClass]
    public class BLLAdaptiveMapperDataTypesUnitTests
    {
        #region db creation and clean up
        //these two members are used only in CreateDB and DropDB methods
        //production code and test methods take this setting from the config file
        const string connectionStringToServer = @"Data Source=.;Integrated Security=True;Pooling=false";
        static string connectionStringToDatabase = "Data Source=.;Initial Catalog=" + dbName + ";Integrated Security=True;Pooling=false";
        const string dbName = "TestDB";
        const DBMSTypes dbType = DBMSTypes.MSSQL;

        public string ChangeDBInConnectionString(string connectionString, string databaseName)
        {
            SqlConnectionStringBuilder csb = new SqlConnectionStringBuilder(connectionString);
            csb.InitialCatalog = databaseName;
            return csb.ConnectionString;
        }

        public bool TryConnect(string connectionString)
        {
            bool result = false;

            SqlConnection dbmsCon = new SqlConnection(connectionString);
            try
            {
                dbmsCon.Open();
                result = true;
            }
            catch
            {
                result = false;
            }
            finally
            {
                if (dbmsCon != null) dbmsCon.Dispose();
            }
            return result;
        }

        public void CreateDB(string connectionString, string dbName)
        {
            string connectionStringToDataBase = ChangeDBInConnectionString(connectionString, dbName);
            string connectionStringToServer = ChangeDBInConnectionString(connectionString, "master");
            if (TryConnect(connectionStringToDataBase) == false)
                using (SqlConnection con = new SqlConnection(connectionStringToServer))
                {
                    con.Open();
                    SqlCommand cmdCheck = new SqlCommand(String.Format(@"CREATE DATABASE [{0}]", dbName), con);
                    cmdCheck.ExecuteScalar();
                }
            //it takes some time server to create database. do not return method until db has created. 
            //otherwise if some method followed this one will try to connect to db exception occure 
            while (TryConnect(connectionStringToDataBase) == false)
                System.Threading.Thread.Sleep(10);
        }

        public void DropDB(string connectionString, string dbName)
        {
            string connectionStringToServer = ChangeDBInConnectionString(connectionString, "master");
            using (SqlConnection con = new SqlConnection(connectionStringToServer))
            {
                con.Open();
                SqlCommand cmdCheck = new SqlCommand(String.Format(@"DROP DATABASE [{0}]", dbName), con);
                cmdCheck.ExecuteScalar();
            }
        }

        [TestInitialize]
        public void TestInit()
        {

            CreateDB(connectionStringToServer, dbName);
        }

        [TestCleanup]
        public void TestClean()
        {
            DropDB(connectionStringToServer, dbName);
        }

        #endregion


        [TableAttribute(TableName = "Parent")]
        public class ParentTestBLL : BLLSuperType
        {
            [PrimaryKeyAttribute("RowID", 0)]
            public Guid ID { get; set; }

            [FieldAttribute(FieldName = "SomeField1", Attributes=new object[] { 12, 5 })]
            public Decimal DecimalPropCustomNameAndSize { get; set; }

            [FieldAttribute(FieldName = "SomeField2")]
            public Decimal DecimalPropCustomName { get; set; }

            [FieldAttribute]
            public Decimal DecimalProperty { get; set; }

            [FieldAttribute]
            public Decimal? DecimalPropertyNullable { get; set; }

            [FieldAttribute]
            public string StringProperty { get; set; }

            [FieldAttribute(Attributes=new object[] { 64 })]
            public string StringPropertyCustomSize { get; set; }

            [FieldAttribute]
            public bool BoolProperty { get; set; }

            [FieldAttribute]
            public bool? BoolPropertyNullable { get; set; }

            [FieldAttribute]
            public DateTime DateTimeProperty { get; set; }

            [FieldAttribute]
            public DateTime? DateTimePropertyNullable { get; set; }

            [FieldAttribute]
            public Double DoubleProperty { get; set; }

            [FieldAttribute]
            public Double? DoublePropertyNullable { get; set; }

            [FieldAttribute]
            public Single SingleProperty { get; set; }

            [FieldAttribute]
            public Single? SinglePropertyNullable { get; set; }

            [FieldAttribute]
            public Int16 Int16Property { get; set; }

            [FieldAttribute]
            public Int16? Int16PropertyNullable { get; set; }

            [FieldAttribute]
            public Int32 Int32Property { get; set; }

            [FieldAttribute]
            public Int32? Int32PropertyNullable { get; set; }

            [FieldAttribute]
            public Int64 Int64Property { get; set; }

            [FieldAttribute]
            public Int64? Int64PropertyNullable { get; set; }

            [FieldAttribute]
            public Guid GuidProperty { get; set; }

            [FieldAttribute]
            public Guid? GuidPropertyNullable { get; set; }

            private List<ChildTestBLL> relatedBLLObjs = new List<ChildTestBLL>();
            [DebuggerBrowsable(DebuggerBrowsableState.Never)]
            public List<ChildTestBLL> RelatedBLLObjs
            {
                get
                {
                    Load(new BLLAdaptiveMapper(connectionStringToDatabase, dbType));
                    return this.relatedBLLObjs;
                }
                set
                {
                    Load(new BLLAdaptiveMapper(connectionStringToDatabase, dbType));
                    this.relatedBLLObjs = value;
                }
            }
        }

        [TableAttribute(TableName = "Child")]
        [ForeignKeyDeclarationAttribute(typeof(ParentTestBLL), RelationQuantity.OneOrZeroToManyOrZero, "RelatedBLLObjs", 0)]
        public class ChildTestBLL : BLLSuperType
        {
            [PrimaryKeyAttribute("RowID", 0)]
            public Guid ID { get; set; }

            [FieldAttribute(FieldName = "SomeField")]
            public int IntProperty { get; set; }

            [FieldAttribute]
            public string StringProperty { get; set; }

        }


        [TestMethod]
        public void SaveBatchBLLObjTestParentChildDataTypes()
        {
            //if referenced db table is not created DynORM won't create FK constraint and create it as soon as parent table will be created

            BLLAdaptiveMapper mapper = new BLLAdaptiveMapper(connectionStringToDatabase, dbType);
            Guid id = Guid.NewGuid();
            ChildTestBLL bllObj = (ChildTestBLL)mapper.Create(typeof(ChildTestBLL), id);
            bllObj.IntProperty = 5;
            bllObj.StringProperty = "9";
            bllObj.MarkAsLoaded();

            Guid parentid = Guid.NewGuid();
            ParentTestBLL parentbllObj = (ParentTestBLL)mapper.Create(typeof(ParentTestBLL), parentid);
            parentbllObj.DecimalPropCustomNameAndSize = 15;
            parentbllObj.DecimalPropCustomName = 17.7m;
            parentbllObj.DecimalProperty = 15.5m;
            parentbllObj.StringPropertyCustomSize = "test";
            parentbllObj.BoolProperty = true;
            parentbllObj.DateTimeProperty = DateTime.Now;
            parentbllObj.DoubleProperty = 19.3;
            parentbllObj.SingleProperty = 19.3f;
            parentbllObj.Int16Property = 16;
            parentbllObj.Int32Property = 32;
            parentbllObj.Int64Property = 64;
            parentbllObj.GuidProperty = Guid.Empty;
            parentbllObj.MarkAsLoaded();
            parentbllObj.RelatedBLLObjs.Add(bllObj);

            mapper.WriteBatch(new object[] { parentbllObj, bllObj }, true);

            mapper.DeleteCascade(parentbllObj);
        }

        [TestMethod]
        public void ReadBLLObjTestParentChildDataTypes()
        {
            BLLAdaptiveMapper mapper = new BLLAdaptiveMapper(connectionStringToDatabase, dbType);
            Guid id = Guid.NewGuid();
            ChildTestBLL bllObj = (ChildTestBLL)mapper.Create(typeof(ChildTestBLL), id);
            bllObj.IntProperty = 5;
            bllObj.StringProperty = "9";
            bllObj.MarkAsLoaded();

            Guid parentid = Guid.NewGuid();
            ParentTestBLL parentbllObj = (ParentTestBLL)mapper.Create(typeof(ParentTestBLL), parentid);
            parentbllObj.DecimalPropCustomNameAndSize = 15;
            parentbllObj.DecimalPropCustomName = 17.7m;
            parentbllObj.DecimalProperty = 15.5m;
            parentbllObj.StringPropertyCustomSize = "test";
            parentbllObj.BoolProperty = true;
            parentbllObj.DateTimeProperty = DateTime.Now;
            parentbllObj.DoubleProperty = 19.3;
            parentbllObj.SingleProperty = 19.3f;
            parentbllObj.Int16Property = 16;
            parentbllObj.Int32Property = 32;
            parentbllObj.Int64Property = 64;
            parentbllObj.GuidProperty = Guid.Empty;
            parentbllObj.MarkAsLoaded();
            parentbllObj.RelatedBLLObjs.Add(bllObj);

            mapper.WriteBatch(new object[] { parentbllObj, bllObj }, true);

            ParentTestBLL retrievedParent = (ParentTestBLL)mapper.Read(typeof(ParentTestBLL), parentid);
            Assert.AreEqual<decimal>(parentbllObj.DecimalPropCustomNameAndSize, retrievedParent.DecimalPropCustomNameAndSize);
            Assert.AreEqual<string>(parentbllObj.StringProperty, retrievedParent.StringProperty);
            Assert.AreEqual<bool>(parentbllObj.BoolProperty, retrievedParent.BoolProperty);
            Assert.AreEqual<bool?>(null, retrievedParent.BoolPropertyNullable);
            Assert.AreEqual<DateTime>(parentbllObj.DateTimeProperty.Date, retrievedParent.DateTimeProperty.Date);
            Assert.AreEqual<DateTime?>(null, retrievedParent.DateTimePropertyNullable);
            Assert.AreEqual<Double>(parentbllObj.DoubleProperty, retrievedParent.DoubleProperty);
            Assert.AreEqual<Double?>(null, retrievedParent.DoublePropertyNullable);
            Assert.AreEqual<Single>(parentbllObj.SingleProperty, retrievedParent.SingleProperty);
            Assert.AreEqual<Single?>(null, retrievedParent.SinglePropertyNullable);
            Assert.AreEqual<Int16>(parentbllObj.Int16Property, retrievedParent.Int16Property);
            Assert.AreEqual<Int16?>(null, retrievedParent.Int16PropertyNullable);
            Assert.AreEqual<Int32>(parentbllObj.Int32Property, retrievedParent.Int32Property);
            Assert.AreEqual<Int32?>(null, retrievedParent.Int32PropertyNullable);
            Assert.AreEqual<Int64>(parentbllObj.Int64Property, retrievedParent.Int64Property);
            Assert.AreEqual<Int64?>(null, retrievedParent.Int64PropertyNullable);
            Assert.AreEqual<Guid>(parentbllObj.GuidProperty, retrievedParent.GuidProperty);
            Assert.AreEqual<Guid?>(null, retrievedParent.GuidPropertyNullable);
            Assert.AreEqual<int>(parentbllObj.RelatedBLLObjs[0].IntProperty, retrievedParent.RelatedBLLObjs[0].IntProperty);
            Assert.AreEqual<string>(parentbllObj.RelatedBLLObjs[0].StringProperty, retrievedParent.RelatedBLLObjs[0].StringProperty);

            mapper.DeleteCascade(parentbllObj);
        }

        [TestMethod]
        public void DeleteCascadeBLLObjTestParentChildDataTypes()
        {
            BLLAdaptiveMapper mapper = new BLLAdaptiveMapper(connectionStringToDatabase, dbType);
            Guid id = Guid.NewGuid();
            ChildTestBLL bllObj = (ChildTestBLL)mapper.Create(typeof(ChildTestBLL), id);
            bllObj.IntProperty = 5;
            bllObj.StringProperty = "9";
            bllObj.MarkAsLoaded();

            Guid parentid = Guid.NewGuid();
            ParentTestBLL parentbllObj = (ParentTestBLL)mapper.Create(typeof(ParentTestBLL), parentid);
            parentbllObj.DecimalPropCustomNameAndSize = 15;
            parentbllObj.DecimalPropCustomName = 17.7m;
            parentbllObj.DecimalProperty = 15.5m;
            parentbllObj.StringPropertyCustomSize = "test";
            parentbllObj.BoolProperty = true;
            parentbllObj.DateTimeProperty = DateTime.Now;
            parentbllObj.DoubleProperty = 19.3;
            parentbllObj.SingleProperty = 19.3f;
            parentbllObj.Int16Property = 16;
            parentbllObj.Int32Property = 32;
            parentbllObj.Int64Property = 64;
            parentbllObj.GuidProperty = Guid.Empty;
            parentbllObj.MarkAsLoaded();
            parentbllObj.RelatedBLLObjs.Add(bllObj);

            mapper.WriteBatch(new object[] { parentbllObj, bllObj }, true);

            mapper.DeleteCascade(parentbllObj);

            ParentTestBLL retrievedParent = (ParentTestBLL)mapper.Read(typeof(ParentTestBLL), parentid);
            ChildTestBLL retrievedChild = (ChildTestBLL)mapper.Read(typeof(ChildTestBLL), id);
            Assert.IsNull(retrievedParent);
            Assert.IsNull(retrievedChild);
        }
    }
}
